#include <time.h>
#include "include/rw_functions.h"
#include "include/rw_png.h"

#define true 1
#define false 0

void printMatriz( int *mat, int Size );

int main( int argc, char* argv[] )
{
    char saida[50] = "arquivo_saida.png";   /* Nome de saida padrão para a imagem a gerada */
    int opcao = 0;                      /* Valor padrão, Indica execução Serial */
    int Size = 1024;                    /* Valor padrão, Dimenssão da matriz configurada como 1024 x 1024 */
    int numThreads = 1;                 /* Valor padrão, 1 Thread de Execução */
    int numParticulas = 1024;           /* Valor padrão, 1024 Particulas lançadas */
    int *matriz = NULL;
    //int **mat = NULL;
    
    
    /* 
     * argv[0] = Nome do arquivo (Padrão do C) 
     * argv[1] = indica qual modulo será execultado, 0 = Serial, 1 = Paralelo OpenMp Atomic, 2 = Paralelo OpenMp Reduction
     * argv[2] = Tamanho da Matriz ( Matriz Quadrada ) Assumimos ser Multiplo de 2
     * argv[3] = Número de Particulas do esperimento
     * argv[4] = Nome do arquivo de SAIDA (Imagem)
     * argv[5] = (Parametro Opcional) número de threads utilizadas obs = Padrão é 0 Threads
     */
    
    printf("Quantidade de Argumentos passados: %d\n", argc );
    
    if ( argc == 1 ) {
        printf("Executando Experimento Padrão\n");
        printf("Formato de execução do programa: ./[EXECUTÁVEL] [OPÇÃO] [SIZE] [PARTICULAS] [NOME_ARQUIVO] [QUANT_THREADS]\n\n");
        printf("Onde:\n");
        printf("[OPÇÃO]         -  [0] Para Execução Serial, [1] Execução Paralelo Atomic, [2] Execução Paralelo Reduction\n");
        printf("[SIZE]          -  Tamanho do domínio\n");
        printf("[PARTICULAS]    -  Quantidade de Particulas\n");
        printf("[NOME_ARQUIVO]  -  Nome do aquivo de saida\n");
        printf("[QUANT_THREADS] -  Quantidade de Threads de Execução\n");
        exit(0);
    }
    
    if ( argc > 1 ){
        /* Pega a opção, [0] Para Serial, [1] para Paralelo atomic, [2] para paralelo reduction */
        opcao = atoi(argv[1]);
    
        if ( argc > 2 ){
            /* Pega o tamanho da matriz */ 
            Size = atoi(argv[2]);
            
            if ( argc > 3 ){
                /* Recupera o número de particulas */
                numParticulas =  atoi(argv[3]);
                
                if ( argc > 4 ){
                    /* Recupera o nome do arquivo de saida */
                    strcpy( saida, argv[4] );
                    
                    if ( argc > 5 ) {
                        /* Recupera o número de Threads Apenas se for especificado */
                        numThreads = atoi( argv[5] );
                    }
                }
            }
        }
    }
    
    //Inicializando a semente do gerador de numeros pseudo aleatorios
    srand(time(NULL));
    
    initMTRefCPU("MersenneTwister.raw");//inicializa a estrutura de números aleaorios do MT
    
    unsigned int seed;

    seed = rand(); 
 
    /* Execulta o RandomWalk Serial */
    switch(opcao)
    {
        /* Execução Serial */
        case 0:
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            printf("RW-Serial\nSize: %d x %d\nParticulas: %d\n\n", Size, Size, numParticulas );
            matriz = rw_serial( numParticulas, Size, seed );
            printf("Chegaram: %d\n", matriz[Size*Size-1] );
            printf("Probabilidade: %f\n", (matriz[Size*Size-1] / (double)numParticulas) );
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n");
            break;
          
        /* Execução Paralela OpenMp Atomic */  
        case 1:
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            printf("aquiiii RW-Paralelo Atomic\nSize: %d x %d\nParticulas: %d\nThreads: %d\n\n", Size, Size, numParticulas, numThreads );
            matriz = rw_pOpenMP( numParticulas, Size, seed, numThreads );
            printf("Chegaram: %d\n", matriz[Size*Size-1] );
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n");
            break;
            
        /* Execução Paralela OpenMp Redução */   
        case 2:
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            printf("RW-Paralelo Reduction\nSize: %d x %d\nParticulas: %d\nThreads: %d\n\n", Size, Size, numParticulas, numThreads );
            matriz = rw_parallel_reduction( numParticulas, Size, seed, numThreads );
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n");
            break;
	    
	    /* Execução Paralela OpenMp Critical */  
        case 3:
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            printf("RW-Paralelo Critical\nSize: %d x %d\nParticulas: %d\nThreads: %d\n\n", Size, Size, numParticulas, numThreads );
            matriz = rw_pOpenMPCrit( numParticulas, Size, seed, numThreads );
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n");
            break;
        
        case 4:
            /* Aqui a variável numParticulas corresponde a taxa de incremento de particulas */
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            printf("RW-Serial Iterativo\nSize: %d x %d\n\n", Size, Size );
            matriz = rw_serial_iterativo( numParticulas, Size, seed );
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n");
            break;
        
        case 5:
            /* Aqui a variável numParticulas corresponde a taxa de incremento de particulas */
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
            printf("RW-Paralelo Iterativo\nSize: %d x %d\nTaxa de Incremento: %d\n\n", Size, Size, numParticulas );
            matriz = rw_pOpenMP_iterativo( numParticulas, Size, seed, numThreads );
            printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n");
            break;
    }
    
    
    int result = 0;
    if ( matriz != NULL )
        result = writeImage( saida, matriz, "This is a Random Walk result's image", Size);
    
    free(matriz);
    
    return result;
}


void printMatriz( int *mat, int Size )
{
    if ( mat == NULL ){
        printf("Matriz NULL\n");
        return;
    }
    
    int i, j;
    for ( i = 0; i < Size; i++ ){
        for ( j = 0; j < Size; j++ ){
            if ( mat[i * Size + j] == 0 )
                printf("   ");
            else
                printf("%3d", mat[i * Size + j] );
            }
        printf("\n");
    }
}

